#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_array.h"
#include "im_value.h"
#include "im_value_impl.h"
#include "im_value_array.h"

/************************** Declaration of internal functions ***************/
static void     __im_value_array_initlaize      (IMPointer      array);
static void     __im_value_array_finalize       (IMPointer      array);
static void     __im_value_array_copy           (IMPointer      dest,
                                                 IMConstPointer src);
static IMBool   __im_value_array_serialize      (IMConstPointer obj,
                                                 IMPointer      stream);
static IMBool   __im_value_array_deserialize    (IMPointer      obj,
                                                 IMPointer      stream);
static void     __im_value_array_class_init     (IMPointer      klass);

/****************** Declaration of object and class structure ************/
struct _IMValueArray
{
    IMObject object;

    /**< private >**/
    IMArray  *array;
};

struct _IMValueArrayClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_value_array_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_value_array_initialize (IMPointer array)
{
    ((IMValueArray*)array)->array = im_array_new (FALSE, FALSE, sizeof (IMValueImpl));
}

static void
__im_value_array_finalize (IMPointer array)
{
    IMValueArray *ap = IM_VALUE_ARRAY (array);
    IMSize i;

    _im_assert (ap != 0);

    for (i=0; i<im_array_size (ap->array); ++i)
        im_value_impl_clear (& im_array_index (ap->array, IMValueImpl, i));

    im_array_destroy (ap->array, TRUE);
}

static void
__im_value_array_copy (IMPointer dest, IMConstPointer src)
{
    IMValueArray       *dap = IM_VALUE_ARRAY (dest);
    const IMValueArray *sap = IM_CONST_VALUE_ARRAY (src);

    _im_assert (dap != 0 && sap != 0);

    im_value_array_clear (dap);

    if (im_array_size (sap->array)) {
        IMSize i;
        IMValueImpl impl;

        im_array_reserve (dap->array, im_array_size (sap->array));

        for (i=0; i<im_array_size (sap->array); ++i) {
            im_value_impl_init (&impl);
            im_value_impl_copy (&impl, &im_array_index (sap->array, IMValueImpl, i));
            im_array_append_val(dap->array, impl);
        }
    }

    /* Call copy method of parent class */
    __im_value_array_parent_class->copy (dest, src);
}

static IMBool
__im_value_array_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMValueArray *ap = IM_CONST_VALUE_ARRAY (obj);
    IMStream            *sp = IM_STREAM (stream);
    IMSize               i;

    _im_assert (ap != 0 && sp != 0);

    /* Call parent serialize method */
    if (!__im_value_array_parent_class->serialize (obj, stream))
        return FALSE;
    if (!im_stream_put_uint32 (sp, im_array_size (ap->array)))
        return FALSE;

    for (i = 0; i < im_array_size (ap->array); ++i)
        if (!__im_value_impl_serialize (&im_array_index (ap->array, IMValueImpl, i), sp))
            return FALSE;
    return TRUE;
}

static IMBool
__im_value_array_deserialize (IMPointer obj, IMPointer stream)
{
    IMValueArray   *ap = IM_VALUE_ARRAY (obj);
    IMStream       *sp = IM_STREAM (stream);
    IMSize          i;
    IMUInt32        n = 0;
    IMValueImpl     impl;

    _im_assert (ap != 0 && sp != 0);

    /* Call parent deserialize method */
    if (!__im_value_array_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT32 || !im_stream_get_uint32 (sp, &n)) 
        return FALSE;

    im_value_array_clear (ap);

    im_array_reserve (ap->array, n);

    for (i = 0; i < n; ++i) {
        im_value_impl_init (&impl);

        if (!__im_value_impl_deserialize (&impl, sp))
            return FALSE;

        im_array_append_val (ap->array, impl);
    }

    return TRUE;
}

static void
__im_value_array_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_value_array_copy;
    cp->serialize   = __im_value_array_serialize;
    cp->deserialize = __im_value_array_deserialize;

    __im_value_array_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_value_array ()
{
    static IMTypeInfo im_value_array_type_info = 
    {
        sizeof (IMValueArrayClass), /**< class_size >**/
        __im_value_array_class_init,/**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMValueArray),      /**< instance_size >**/
        __im_value_array_initialize,/**< instance_init >**/
        __im_value_array_finalize,  /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_VALUE_ARRAY,
                                      "ValueArray",
                                      &im_value_array_type_info,
                                      TRUE);
}

IMValueArray*
im_value_array_new ()
{
    IMValueArray *a = (IMValueArray *) im_object_new (IM_TYPE_VALUE_ARRAY);

    _im_assert (a);

    return a;
}

void
im_value_array_clear (IMValueArray *array)
{
    IMSize i;
    _im_return_if_fail (IM_IS_VALUE_ARRAY (array));

    for (i=0; i<im_array_size (array->array); ++i)
        im_value_impl_clear (& im_array_index (array->array, IMValueImpl, i));

    im_array_clear (array->array);
}

void
im_value_array_reserve (IMValueArray *array, IMSize space)
{
    _im_return_if_fail (IM_IS_VALUE_ARRAY (array));

    im_array_reserve (array->array, space);
}

IMSize
im_value_array_size (const IMValueArray *array)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);

    return im_array_size (array->array);
}

IMType
im_value_array_get_value_type (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), IM_TYPE_INVALID);
    _im_return_val_if_fail (index < im_array_size (array->array), IM_TYPE_INVALID);

    return im_value_impl_value_type (&im_array_index (array->array, IMValueImpl, index));
}

IMType
im_value_array_get (const IMValueArray *array, IMSize index, IMValue *value)
{
    _im_return_if_fail (IM_IS_VALUE_ARRAY (array));
    _im_return_if_fail (IM_IS_VALUE (value));
    _im_return_if_fail (index < im_array_size (array->array));

    im_value_impl_copy (__im_value_get_impl (value),
                        &im_array_index(array->array, IMValueImpl, index));

    return im_value_impl_value_type (&im_array_index(array->array, IMValueImpl, index));
}

void
im_value_array_set (IMValueArray *array, IMSize index, IMValue *value)
{
    _im_return_if_fail (IM_IS_VALUE_ARRAY (array));
    _im_return_if_fail (IM_IS_VALUE (value));
    _im_return_if_fail (index < im_array_size (array->array));

    im_object_ref (value);

    im_value_impl_copy (&im_array_index(array->array, IMValueImpl, index),
                         __im_value_get_impl (value));

    im_object_unref (value);
}

IMValueArray*
im_value_array_prepend (IMValueArray *array, IMValue *value)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_VALUE (value), 0);

    im_object_ref (value);
    im_value_impl_init (&impl);
    im_value_impl_copy (&impl, __im_value_get_impl (value));
    im_array_prepend_val (array->array, impl);
    im_object_unref (value);

    return array;
}

IMValueArray*
im_value_array_append (IMValueArray *array, IMValue *value)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_VALUE (value), 0);

    im_object_ref (value);
    im_value_impl_init (&impl);
    im_value_impl_copy (&impl, __im_value_get_impl (value));
    im_array_append_val (array->array, impl);
    im_object_unref (value);

    return array;
}

IMValueArray*
im_value_array_insert (IMValueArray *array, IMSize index, IMValue *value)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_VALUE (value), 0);
    _im_return_val_if_fail (index <= im_array_size (array->array), 0);

    im_object_ref (value);
    im_value_impl_init (&impl);
    im_value_impl_copy (&impl, __im_value_get_impl (value));
    im_array_insert_val (array->array, index, impl);
    im_object_unref (value);

    return array;
}

IMValueArray*
im_value_array_prepend_object (IMValueArray *array, IMPointer object)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);

    im_value_impl_init (&impl);
    im_value_impl_set_object (&impl, object);
    im_array_prepend_val (array->array, impl);

    return array;
}

IMValueArray*
im_value_array_append_object (IMValueArray *array, IMPointer object)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);

    im_value_impl_init (&impl);
    im_value_impl_set_object (&impl, object);
    im_array_append_val (array->array, impl);

    return array;
}

IMValueArray*
im_value_array_insert_object (IMValueArray *array, IMSize index, IMPointer object)
{
    IMValueImpl impl;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);
    _im_return_val_if_fail (index <= im_array_size (array->array), 0);

    im_value_impl_init (&impl);
    im_value_impl_set_object (&impl, object);
    im_array_insert_val (array->array, index, impl);

    return array;
}

IMValueArray*
im_value_array_remove (IMValueArray *array, IMSize index)
{
    IMPointer object;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);

    im_value_impl_clear (&im_array_index(array->array, IMValueImpl, index));

    im_array_remove_index (array->array, index);

    return array;
}

IMValueArray*
im_value_array_remove_fast (IMValueArray *array, IMSize index)
{
    IMPointer object;

    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);

    im_value_impl_clear (&im_array_index(array->array, IMValueImpl, index));

    im_array_remove_index_fast (array->array, index);

    return array;
}

typedef struct
{
    IMValue   *val;
    IMFunc     func;
    IMPointer  user_data;
} __IMValueArrayForeachData;

static void
__im_value_array_foreach_func (IMPointer data, IMPointer user_data)
{
    __IMValueArrayForeachData *p = (__IMValueArrayForeachData*) user_data;

    __im_value_set_impl (p->val, (const IMValueImpl*) data);

    p->func (p->val, p->user_data);
}

void
im_value_array_foreach (const IMValueArray *array, IMFunc func, IMPointer user_data)
{
    __IMValueArrayForeachData data;

    _im_return_if_fail (IM_IS_VALUE_ARRAY (array));
    _im_return_if_fail (func);

    data.val = im_value_new ();
    data.func = func;
    data.user_data = user_data;

    im_object_ref (data.val);

    im_array_foreach (array->array, __im_value_array_foreach_func, &data);

    __im_value_set_impl (data.val, 0);

    im_object_unref (data.val);
}

IMChar
im_value_array_get_char (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_char (&im_array_index(array->array, IMValueImpl, index));
}
IMUChar
im_value_array_get_uchar (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_uchar (&im_array_index(array->array, IMValueImpl, index));
}
IMBool
im_value_array_get_bool (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), FALSE);
    _im_return_val_if_fail (index < im_array_size (array->array), FALSE);
    return im_value_impl_get_bool (&im_array_index(array->array, IMValueImpl, index));
}
IMInt16
im_value_array_get_int16 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_int16 (&im_array_index(array->array, IMValueImpl, index));
}
IMUInt16
im_value_array_get_uint16 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_uint16 (&im_array_index(array->array, IMValueImpl, index));
}
IMInt32
im_value_array_get_int32 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_int32 (&im_array_index(array->array, IMValueImpl, index));
}
IMUInt32
im_value_array_get_uint32 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_uint32 (&im_array_index(array->array, IMValueImpl, index));
}
IMInt64
im_value_array_get_int64 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_int64 (&im_array_index(array->array, IMValueImpl, index));
}
IMUInt64
im_value_array_get_uint64 (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_uint64 (&im_array_index(array->array, IMValueImpl, index));
}
IMDouble
im_value_array_get_double (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_double (&im_array_index(array->array, IMValueImpl, index));
}
IMPointer
im_value_array_get_pointer (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_pointer (&im_array_index(array->array, IMValueImpl, index));
}
const IMChar*
im_value_array_get_c_string (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_c_string (&im_array_index(array->array, IMValueImpl, index));
}
IMChar*
im_value_array_dup_c_string (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_dup_c_string (&im_array_index(array->array, IMValueImpl, index));
}
IMPointer
im_value_array_get_object (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_get_object (&im_array_index(array->array, IMValueImpl, index));
}
IMPointer
im_value_array_dup_object (const IMValueArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_VALUE_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_array_size (array->array), 0);
    return im_value_impl_dup_object (&im_array_index(array->array, IMValueImpl, index));
}


/*
vi:ts=4:nowrap:ai:expandtab
*/
